package com.yindong.camera.slice.init;

import com.yindong.camera.slice.CameraInfoConfig;
import com.yindong.camera.util.CameraRatioUtil;
import com.yindong.camera.util.EmptyUtil;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.app.Context;
import ohos.app.Environment;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.*;
import ohos.media.common.AudioProperty;
import ohos.media.common.Source;
import ohos.media.common.StorageProperty;
import ohos.media.common.VideoProperty;
import ohos.media.image.Image;
import ohos.media.image.ImageReceiver;
import ohos.media.image.common.ImageFormat;
import ohos.media.recorder.Recorder;

import java.io.File;
import java.util.UUID;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_RECORD;

/**
 * 相机初始化类
 *
 * @author      殷冬
 * @date        2021-01-31
 * @version     1.0.0
 */
public class CameraInit {

    private static final HiLogLabel LOG = new HiLogLabel(HiLog.LOG_APP, 00201, CameraInit.class.getName());

    /**
     * 相机运行线程名
     */
    private static final String EVENT_RUNNER_NAME = "LocalCamera";

    private static final String FRAME_RUNNER_NAME = "FrameCamera";

    /**
     * cameraKit对象
     */
    private static CameraKit cameraKit;

    /**
     * 预览surface
     */
    private Surface previewSurface;

    /**
     * 图像帧数据接收处理对象
     */
    private static ImageReceiver imageReceiver;

    /**
     * 相机属性
     */
    private CameraConfig.Builder cameraConfigBuilder;

    /**
     * 预览模板
     */
    private static FrameConfig.Builder frameConfigBuilder;

    /**
     * 录像操作对象
     */
    private Recorder mediaRecorder;

    private Context context;

    private static Camera myCamera;

    private SurfaceOps surfaceOps;

    public CameraInit(Context context, Surface surface, SurfaceOps surfaceOps){
        this.context = context;
        this.previewSurface = surface;
        this.surfaceOps = surfaceOps;
    }

    /**
     * 创建相机
     *
     * @return
     */
    public boolean createCamera(){
        // 创建cameraKit对象
        boolean createKitState = this.createCameraKit(context);
        // 获取相机列表
        String[] cameraIds = this.getCameraIds();
        if(! createKitState || EmptyUtil.isEmpty(cameraIds)){
            // 创建失败
            return false;
        }
        // 根据使用的前或后相机，获取cameraAbility对象
        CameraAbility cameraAbility = cameraKit.getCameraAbility(cameraIds[CameraInfoConfig.cameraType]);
        // 获取拍照支持的分辨率列表
        CameraInfoConfig.pictureSizes = cameraAbility.getSupportedSizes(ImageFormat.JPEG);
        // 获取最佳分辨率
        CameraInfoConfig.pictureOptimumSize = CameraRatioUtil.getOptimalPreviewSize(true, CameraInfoConfig.pictureSizes, CameraInfoConfig.cameraSize.width, CameraInfoConfig.cameraSize.height);
        // 设置预览surface属性
        this.setSurface(previewSurface);
        // 初始化图像接收器对象
        this.initImageReceiver();
        // 创建相机对象，会调用cameraStateCallback中的回调方法
        cameraKit.createCamera(cameraIds[CameraInfoConfig.cameraType], new CameraCallback(),  new EventHandler(EventRunner.create(EVENT_RUNNER_NAME)));
        return true;
    }

    /**
     * 创建CameraKit对象
     *
     * @param context   上下文
     * @return
     */
    private boolean createCameraKit(Context context){
        // 获取CameraKit实例
        cameraKit = CameraKit.getInstance(context);
        if(cameraKit == null){
            HiLog.info(LOG, "--------------------CameraKit实例为空");
            return false;
        }
        return true;
    }

    /**
     * 获取相机id数组
     *
     * @return
     */
    private String[] getCameraIds(){
        String[] ids = null;
        try{
            ids = cameraKit.getCameraIds();
        }catch (IllegalStateException e){
            HiLog.info(LOG, "--------------------未初始化相机服务: %{public}s", e);
        }
        return ids;
    }

    /**
     * surface设置
     *
     * @param surface       surface对象
     */
    private void setSurface(Surface surface){
        // 设置原始图像数据格式、大小
        surface.showRawImage(
                new byte[Surface.PixelFormat.PIXEL_FORMAT_YCBCR_422_I.value() + CameraInfoConfig.pictureOptimumSize.width + CameraInfoConfig.pictureOptimumSize.height],
                Surface.PixelFormat.PIXEL_FORMAT_YCBCR_422_I,
                CameraInfoConfig.pictureOptimumSize.width,
                CameraInfoConfig.pictureOptimumSize.height);
        // 同步画布对象上的绘制调用以进行呈现。
        surface.syncCanvasDrawCalls();
    }

    private void initImageReceiver(){
        //  创建ImageReceiver对象，注意create函数中宽度要大于高度；5为最大支持的图像数，请根据实际设置。
        imageReceiver = ImageReceiver.create(Math.max(CameraInfoConfig.pictureOptimumSize.width, CameraInfoConfig.pictureOptimumSize.height),
                Math.min(CameraInfoConfig.pictureOptimumSize.width, CameraInfoConfig.pictureOptimumSize.height),
                ImageFormat.JPEG, 5);
        imageReceiver.setImageArrivalListener(imageArrivalListener);
    }

    public static void capture() {
        // 获取拍照配置模板
        frameConfigBuilder = myCamera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PICTURE);
        // 配置拍照Surface
        frameConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        // 配置拍照其他参数
        frameConfigBuilder.setImageRotation(90);
        try {
            // 启动单帧捕获(拍照)
            myCamera.triggerSingleCapture(frameConfigBuilder.build());
        } catch (IllegalArgumentException e) {
            HiLog.error(LOG, "Argument Exception");
        } catch (IllegalStateException e) {
            HiLog.error(LOG, "State Exception");
        }
    }

    /**
     * 单帧捕获生成图像回调Listener
     */
    private final ImageReceiver.IImageArrivalListener imageArrivalListener = new ImageReceiver.IImageArrivalListener() {
        @Override
        public void onImageArrival(ImageReceiver imageReceiver) {
            // 设置图片名
            StringBuffer fileName = new StringBuffer("picture_");
            fileName.append(UUID.randomUUID()).append(".jpg");
            File myFile = new File(fileName.toString());
            // 保存图片
            Image image = imageReceiver.readNextImage();
            ImageService imageService = new ImageService(image, myFile);
            EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb"));
            eventHandler.postTask(imageService);
        }
    };

    /**
     * 录像前，配置音视频模块
     */
    private void initMediaRecorder(){
        // 1. 创建Recorder对象
        mediaRecorder = new Recorder();
        // 2. 构造数据源对象
        Source source = new Source();
        // 设置音频源
        source.setRecorderAudioSource(Recorder.AudioSource.MIC);
        // 设置视频源
        source.setRecorderVideoSource(Recorder.VideoSource.SURFACE);
        mediaRecorder.setSource(source);
        // 3. 设置文件格式
        mediaRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4);
        // 4. 构造音频属性对象
        AudioProperty audioProperty = new AudioProperty.Builder()
                .setRecorderNumChannels(2)
                .setRecorderSamplingRate(8000)
                .setRecorderAudioEncoder(Recorder.AudioEncoder.AAC)// AAC
                .build();
        mediaRecorder.setAudioProperty(audioProperty);
        // 5. 构造存储属性
        StringBuffer fileName = new StringBuffer("record_"); // 生成随机文件名
        fileName.append(UUID.randomUUID()).append(".mp4");
        File recorderFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_MOVIES), fileName.toString()); // 创建录像文件对象
        StorageProperty storageProperty = new StorageProperty.Builder()
                .setRecorderFile(recorderFile)
//                .setRecorderPath(context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) + File.separator + fileName.toString())
//                .setRecorderMaxDurationMs(1000000)
//                .setRecorderMaxFileSizeBytes(1000000)
                .build();
        mediaRecorder.setStorageProperty(storageProperty);
        // 6. 构造视频属性
        VideoProperty videoProperty = new VideoProperty.Builder()
                .setRecorderVideoEncoder(Recorder.VideoEncoder.DEFAULT)
                .setRecorderWidth(CameraInfoConfig.cameraSize.width)
                .setRecorderHeight(CameraInfoConfig.cameraSize.height)
                .setRecorderFps(30)
                // 旋转角度
                .setRecorderDegrees(90)
                .setRecorderBitRate(10000000)
                .setRecorderRate(30)
                .build();
        mediaRecorder.setVideoProperty(videoProperty);
        // 7. 准备录制
        mediaRecorder.prepare();
    }

    /**
     * 开始录像
     */
    public void startVideo(){
        frameConfigBuilder = myCamera.getFrameConfigBuilder(FRAME_CONFIG_RECORD);
        // 配置录像的surface
        Surface recorderSurface = mediaRecorder.getVideoSurface();
        frameConfigBuilder.addSurface(recorderSurface);
        // 配置预览surface
        frameConfigBuilder.addSurface(previewSurface);
        try{
            myCamera.triggerLoopingCapture(frameConfigBuilder.build());
        }catch (Exception e){
            HiLog.error(LOG, "启动循环帧捕获异常! %{public}s", e);
        }
        mediaRecorder.start();
    }

    /**
     * 暂停录像
     */
    public void pauseVideo(){
        mediaRecorder.pause();
    }

    /**
     * 恢复录像
     */
    public void resumeVideo(){
        mediaRecorder.resume();
    }

    /**
     * 停止录像
     */
    public void stopVideo(){
        // 停止录像
        mediaRecorder.stop();
        // TODO 通过重新创建相机配置，实现多次录像功能
        // 释放相机
        releaseCamera();
        // 创建相机
        createCamera();
    }

    /**
     * 释放相机资源
     */
    public void releaseCamera() {
        if (myCamera != null) {
            myCamera.stopLoopingCapture();
            // 关闭相机和释放资源
            myCamera.release();
            myCamera = null;
        }
        if(mediaRecorder != null){
            mediaRecorder.release();
            mediaRecorder = null;
        }
        // 预览模板置空
        frameConfigBuilder = null;
        // surface置空
//        previewSurface = null;
    }

    /**
     * 相机创建回调内部类
     *
     * @author      殷冬
     * @date        2021-02-01
     * @version     1.0.0
     */
    class CameraCallback extends CameraStateCallback{

        /**
         * 相机创建成功回调
         *
         * @param camera        相机对象
         */
        @Override
        public void onCreated(Camera camera){
            myCamera = camera;
            // 相机配置
            cameraConfigBuilder =  camera.getCameraConfigBuilder();
            // 设置预览surface
            cameraConfigBuilder.addSurface(previewSurface);
            // 设置拍照surface
            cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
            // 设置录像surface
            cameraConfigBuilder.addSurface(mediaRecorder.getVideoSurface());
            // 配置帧结果的回调
            FrameCallBack frameStateCallBack = new FrameCallBack();
            EventHandler eventHandler = new EventHandler(EventRunner.create(FRAME_RUNNER_NAME));
            cameraConfigBuilder.setFrameStateCallback(frameStateCallBack, eventHandler);
            try {
                // 相机设备配置, 触发onConfigured回调
                camera.configure(cameraConfigBuilder.build());
            } catch (IllegalArgumentException e) {
                HiLog.error(LOG, "Argument Exception");
            } catch (IllegalStateException e) {
                HiLog.error(LOG, "State Exception");
            }
        }

        /**
         * 相机配置回调
         *
         * @param camera        相机对象
         */
        @Override
        public void onConfigured(Camera camera){
            // 获取预览模板
            frameConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
            // 配置预览surface
            frameConfigBuilder.addSurface(previewSurface);
            // 设置放大倍数
//            frameConfigBuilder.setZoom(1.2f);

            FrameConfig previewFrameConfig = frameConfigBuilder.build();
            // 启动帧捕获
            Integer triggerId;
            try {
                // 启动循环帧捕获,获取触发器id
                triggerId = camera.triggerLoopingCapture(previewFrameConfig);
            } catch (IllegalArgumentException e) {
                HiLog.error(LOG, "Argument Exception");
            } catch (IllegalStateException e) {
                HiLog.error(LOG, "State Exception");
            }
        }

        /**
         * 相机创建失败回调
         *
         * @param cameraId      相机id
         * @param errorCode     失败编码
         */
        @Override
        public void onCreateFailed(String cameraId, int errorCode){
            HiLog.info(LOG, "--------------------相机创建失败！相机id: %{public}s; 失败编码: %{public}s;", cameraId, errorCode);

        }

        /**
         * 释放相机对象回调
         *
         * @param camera        相机对象
         */
        @Override
        public void onReleased(Camera camera) {
            // 释放相机设备
            camera.release();
        }
    }

}
